/*
 * {comment of class}
 *
 * @author (C) <a href="http://www.carfield.com.hk">Carfield Yim </a>, 2004
 */
package hk.com.carfield.util;

import hk.com.carfield.core.Config;
import hk.com.carfield.core.Context;
import hk.com.carfield.core.ItemManager;
import hk.com.carfield.item.Item;
import hk.com.carfield.web.Request;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;

public final class FileUtil {
	private final static Calendar cal = Calendar.getInstance();

	public static long cacheExpire(final File location) {
		return location.lastModified() > FileUtil.cal.getTimeInMillis()
				&& location.exists() ? location.lastModified() : FileUtil.cal
				.getTimeInMillis();
	}

	public static SortedSet<Item> getChildred(final Context c, File directory,
			final Request req) throws IOException {
		File[] childs = null;
		do {
			childs = directory.listFiles(c.config.displayFilter());
			directory = directory.getParentFile();
		} while (childs == null);

		final SortedSet<Item> list = ItemManager.getSortedSet();
		for (int i = 0; i < childs.length; i++) {
			if (childs[i].isDirectory()) {
				list.add(ItemManager.getHeadItem(c, childs[i], req, 0));
			} else if (!childs[i].isFile()) {
				LogUtil.error(new Exception("ERROR: " + childs[i]
						+ " is invalid"), c.mailMan, childs[i], req);
			}
		}
		return list;
	}

	public static String getExtensions(final File file) {
		return StringUtil.getExtensions(file.getName());
	}

	public final static String getId(final Config config, final File file) {
		return UrlUtil.getEncodedPath(config, file);
	}

	public static SortedSet<Item> getParents(final Context c,
			final File location, final Request req) throws IOException {
		final SortedSet<Item> list = ItemManager.getSortedSet();
		File parent = location;
		while (c.config.getRoot().getAbsolutePath().length() < parent
				.getAbsolutePath().length()) {
			if (parent.isDirectory()
					&& !parent.getAbsolutePath().contains(
							FileUtil.metaFolderTag())) {
				list.add(ItemManager.getHeadItem(c, parent, req, 0));
			}
			parent = parent.getParentFile();
		}
		return list;
	}

	// FIXME remove
	public static File metaFolder(final File l) {
		final File folder;
		if (l.isDirectory()) {
			folder = new File(l, FileUtil.metaFolderTag());
		} else {
			if (!l.isFile()) {
				LogUtil.log("ERROR: " + l + " is invalid");
			}
			folder = new File(l.getParentFile(), FileUtil.metaFolderTag() + "."
					+ l.getName());
		}
		return folder;
	}

	// FIXME remove
	public static String metaFolderTag() {
		return ".meta";
	}

	public static boolean notFound(final File l) {
		return l.getAbsolutePath().indexOf("..") != -1 || !l.exists() ? true
				: false;
	}

	public static final void setupTestEnv(final String testRoot)
			throws IOException {
		final File realdir = new File(testRoot, "realdir");
		realdir.mkdirs();

		final File testdirZip = new File(realdir, "testdir.zip");
		testdirZip.createNewFile();
		final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(
				testdirZip));
		zos.putNextEntry(new ZipEntry("testdir/testfile.txt"));
		zos.closeEntry();
		zos.close();

		final File muiltbyteDir = new File(new File(testRoot, "中文"), "subdir");
		muiltbyteDir.mkdirs();
		final File testPicture = new File(muiltbyteDir, "test.jpg");
		testPicture.createNewFile();

		FileUtils
				.writeStringToFile(
						new File(testRoot, "test.url"),
						"[DEFAULT] \nBASEURL=http://www.carfield.com.hk/ \n[InternetShortcut] \nURL=http://www.carfield.com.hk/",
						"UTF-8");
		FileUtils
				.writeStringToFile(
						new File(testRoot, "unknown.abc"),
						"AddDescription \"unknown's file\"test\" /unknown.abc\nAddDescription \"\u4e2d\u6587\u5167\u5bb9\" /\u4e2d\u6587/subdir",
						"UTF-8");
		FileUtils
				.writeStringToFile(
						new File(testRoot, "\u4e2d\u6587.txt"),
						"\u4e2d\u6587\nand a long url: http://www.carfield.com.hk/abcdefghijklmnopqrstuvwxyz",
						"UTF-8");
		// FileUtils.writeStringToFile(new File(testRoot, "legacy_encoding.txt")
		// ,
		// "\u4e2d\u6587\nand a long url: http://www.carfield.com.hk/abcdefghijklmnopqrstuvwxyz"
		// , "Big5");
		FileUtils.writeStringToFile(new File(testRoot, "README.html"),
				"README.html", "UTF-8");
		FileUtils.writeStringToFile(new File(testRoot, "HEADER.html"),
				"HEADER.html", "UTF-8");
	}

	public static final String[] unzip(final File file, final File target)
			throws IOException {
		return FileUtil.unzip(new FileInputStream(file), target);
	}

	private static final String[] unzip(final InputStream in, final File target)
			throws IOException {
		final byte[] buffer = new byte[4096];
		final ZipInputStream zis = new ZipInputStream(in);
		try {
			final List<String> entryNames = new ArrayList<String>();
			ZipEntry entry;
			while ((entry = zis.getNextEntry()) != null) {
				if (!entry.isDirectory()) {
					final String entryName = entry.getName();
					final File targetFile = new File(target, entryName);
					entryNames.add(entryName);
					targetFile.getParentFile().mkdirs();
					final FileOutputStream fos = new FileOutputStream(
							targetFile);
					try {
						int bytesRead;
						while ((bytesRead = zis.read(buffer)) != -1) {
							fos.write(buffer, 0, bytesRead);
						}
						fos.flush();
					} finally {
						fos.close();
					}
				}
				zis.closeEntry();
			}
			return entryNames.toArray(new String[0]);
		} finally {
			zis.close();
			in.close();
		}
	}

	public static final boolean valid(final File file) {
		return file.exists() && file.length() > 0;
	}

	public static final boolean zip(final OutputStream os, final File input)
			throws IOException {
		final Set<File> set = new HashSet<File>();
		if (input.isDirectory()) {
			for (final File file : input.listFiles()) {
				if (file.isFile()) {
					set.add(file);
				}
			}
		} else {
			set.add(input);
		}
		if (set.size() == 0) {
			return false;
		}
		final ZipOutputStream out = new ZipOutputStream(os);
		final byte[] buf = new byte[1024];
		try {
			for (final File file : set) {
				final FileInputStream in = new FileInputStream(file);
				out.putNextEntry(new ZipEntry(file.getName()));
				int len;
				while ((len = in.read(buf)) > 0) {
					out.write(buf, 0, len);
				}
				out.closeEntry();
				in.close();
			}
		} finally {
			out.close();
		}
		return true;
	}
}